home *** CD-ROM | disk | FTP | other *** search
- # This module is a lite version of LinAlg.py module which contains
- # high-level Python interface to the LAPACK library. The lite version
- # only accesses the following LAPACK functions: dgesv, zgesv, dgeev,
- # zgeev, dgesvd, zdesvd, dgelss, zgelss.
-
- import Numeric
- import copy
- import lapack_lite
-
- # Error object
- LinAlgError = 'LinearAlgebraError'
-
- # Helper routines
- _lapack_type = {'f': 0, 'd': 1, 'F': 2, 'D': 3}
- _lapack_letter = ['s', 'd', 'c', 'z']
- _array_kind = {'i':0, 'l': 0, 'f': 0, 'd': 0, 'F': 1, 'D': 1}
- _array_precision = {'i': 1, 'l': 1, 'f': 0, 'd': 1, 'F': 0, 'D': 1}
- _array_type = [['f', 'd'], ['F', 'D']]
-
- def _commonType(*arrays):
- kind = 0
- # precision = 0
- # force higher precision in lite version
- precision = 1
- for a in arrays:
- t = a.typecode()
- kind = max(kind, _array_kind[t])
- precision = max(precision, _array_precision[t])
- return _array_type[kind][precision]
-
- def _castCopyAndTranspose(type, *arrays):
- cast_arrays = ()
- for a in arrays:
- if a.typecode() == type:
- cast_arrays = cast_arrays + (copy.copy(Numeric.transpose(a)),)
- else:
- cast_arrays = cast_arrays + (copy.copy(
- Numeric.transpose(a).astype(type)),)
- if len(cast_arrays) == 1:
- return cast_arrays[0]
- else:
- return cast_arrays
-
- def _assertRank2(*arrays):
- for a in arrays:
- if len(a.shape) != 2:
- raise LinAlgError, 'Array must be two-dimensional'
-
- def _assertSquareness(*arrays):
- for a in arrays:
- if max(a.shape) != min(a.shape):
- raise LinAlgError, 'Array must be square'
-
-
- # Linear equations
-
- def solve_linear_equations(a, b):
- one_eq = len(b.shape) == 1
- if one_eq:
- b = b[:, Numeric.NewAxis]
- _assertRank2(a, b)
- _assertSquareness(a)
- n_eq = a.shape[0]
- n_rhs = b.shape[1]
- if n_eq != b.shape[0]:
- raise LinAlgError, 'Incompatible dimensions'
- t =_commonType(a, b)
- # lapack_routine = _findLapackRoutine('gesv', t)
- if _array_kind[t] == 1: # Complex routines take different arguments
- lapack_routine = lapack_lite.zgesv
- else:
- lapack_routine = lapack_lite.dgesv
- a, b = _castCopyAndTranspose(t, a, b)
- pivots = Numeric.zeros(n_eq, 'l')
- results = lapack_routine(n_eq, n_rhs, a, n_eq, pivots, b, n_eq, 0)
- if results['info'] > 0:
- raise LinAlgError, 'Singular matrix'
- if one_eq:
- return copy.copy(Numeric.ravel(b))
- else:
- return copy.copy(Numeric.transpose(b))
-
-
- # Matrix inversion
-
- def inverse(a):
- return solve_linear_equations(a, Numeric.identity(a.shape[0]))
-
- # Eigenvalues
-
- def eigenvalues(a):
- _assertRank2(a)
- _assertSquareness(a)
- t =_commonType(a)
- real_t = _array_type[0][_array_precision[t]]
- a = _castCopyAndTranspose(t, a)
- n = a.shape[0]
- dummy = Numeric.zeros((1,), t)
- lwork = max(1,6*n) # minimum value: max(1,3*n) real, max(1,2*n) complex
- work = Numeric.zeros((lwork,), t)
- if _array_kind[t] == 1: # Complex routines take different arguments
- lapack_routine = lapack_lite.zgeev
- w = Numeric.zeros((n,), t)
- rwork = Numeric.zeros((n,),real_t)
- results = lapack_routine('N', 'N', n, a, n, w,
- dummy, 1, dummy, 1, work, lwork, rwork, 0)
- else:
- lapack_routine = lapack_lite.dgeev
- wr = Numeric.zeros((n,), t)
- wi = Numeric.zeros((n,), t)
- results = lapack_routine('N', 'N', n, a, n, wr, wi,
- dummy, 1, dummy, 1, work, lwork, 0)
- if Numeric.logical_and.reduce(Numeric.equal(wi, 0.)):
- w = wr
- else:
- w = wr+1j*wi
- if results['info'] > 0:
- raise LinAlgError, 'Eigenvalues did not converge'
- return w
-
-
- # Eigenvectors
-
- def eigenvectors(a):
- """eigenvectors(a) returns u,v where u is the eigenvalues and
- v is a matrix of eigenvectors with vector v[i] corresponds to
- eigenvalue u[i]. Satisfies the equation matrixmultiply(a, v[i]) = u[i]*v[i]
- """
- _assertRank2(a)
- _assertSquareness(a)
- t =_commonType(a)
- real_t = _array_type[0][_array_precision[t]]
- a = _castCopyAndTranspose(t, a)
- n = a.shape[0]
- lwork = max(1,8*n) # minimum value: max(1,4*n) real, max(1,2*n) complex
- work = Numeric.zeros((lwork,), t)
- dummy = Numeric.zeros((1,), t)
- if _array_kind[t] == 1: # Complex routines take different arguments
- lapack_routine = lapack_lite.zgeev
- w = Numeric.zeros((n,), t)
- rwork = Numeric.zeros((lwork,),real_t)
- v = Numeric.zeros((n,n), t)
- results = lapack_routine('N', 'V', n, a, n, w,
- dummy, 1, v, n, work, lwork, rwork, 0)
- else:
- lapack_routine = lapack_lite.dgeev
- wr = Numeric.zeros((n,), t)
- wi = Numeric.zeros((n,), t)
- vr = Numeric.zeros((n,n), t)
- results = lapack_routine('N', 'V', n, a, n, wr, wi,
- dummy, 1, vr, n, work, lwork, 0)
- if Numeric.logical_and.reduce(Numeric.equal(wi, 0.)):
- w = wr
- v = vr
- else:
- w = wr+1j*wi
- v = Numeric.array(vr,Numeric.Complex)
- ind = Numeric.nonzero(
- Numeric.equal(
- Numeric.equal(wi,0.0) # true for real e-vals
- ,0) # true for complex e-vals
- ) # indices of complex e-vals
- for i in range(len(ind)/2):
- v[ind[2*i]] = vr[ind[2*i]] + 1j*vr[ind[2*i+1]]
- v[ind[2*i+1]] = vr[ind[2*i]] - 1j*vr[ind[2*i+1]]
- if results['info'] > 0:
- raise LinAlgError, 'Eigenvalues did not converge'
- return w,v
-
-
-
- # Singular value decomposition
-
- def singular_value_decomposition(a, full_matrices = 0):
- _assertRank2(a)
- n = a.shape[1]
- m = a.shape[0]
- t =_commonType(a)
- real_t = _array_type[0][_array_precision[t]]
- a = _castCopyAndTranspose(t, a)
- if full_matrices:
- nu = m
- nvt = n
- option = 'A'
- else:
- nu = min(n,m)
- nvt = min(n,m)
- option = 'S'
- s = Numeric.zeros((min(n,m),), real_t)
- u = Numeric.zeros((nu, m), t)
- lwork = 10*max(m,n) # minimum value: max(3*min(m,n)+max(m,n),5*min(m,n)-4)
- work = Numeric.zeros((lwork,), t)
- vt = Numeric.zeros((n, nvt), t)
- if _array_kind[t] == 1: # Complex routines take different arguments
- lapack_routine = lapack_lite.zgesvd
- rwork = Numeric.zeros((max(3*min(m,n),5*min(m,n)-4),), real_t)
- results = lapack_routine(option, option, m, n, a, m, s, u, m, vt, nvt,
- work, lwork, rwork, 0)
- else:
- lapack_routine = lapack_lite.dgesvd
- results = lapack_routine(option, option, m, n, a, m, s, u, m, vt, nvt,
- work, lwork, 0)
- if results['info'] > 0:
- raise LinAlgError, 'SVD did not converge'
- return copy.copy(Numeric.transpose(u)), s, copy.copy(Numeric.transpose(vt))
-
-
- # Generalized inverse
-
- def generalized_inverse(a, rcond = 1.e-10):
- u, s, vt = singular_value_decomposition(a, 0)
- m = u.shape[0]
- n = vt.shape[1]
- cutoff = rcond*Numeric.maximum.reduce(s)
- for i in range(min(n,m)):
- if s[i] > cutoff:
- s[i] = 1./s[i]
- else:
- s[i] = 0.;
- return Numeric.dot(Numeric.transpose(vt),
- s[:, Numeric.NewAxis]*Numeric.transpose(u))
-
- # Determinant
-
- def determinant(a):
- _assertRank2(a)
- _assertSquareness(a)
- t =_commonType(a)
- a = _castCopyAndTranspose(t, a)
- n = a.shape[0]
- if _array_kind[t] == 1:
- lapack_routine = lapack_lite.zgetrf
- else:
- lapack_routine = lapack_lite.dgetrf
- pivots = Numeric.zeros((n,), 'l')
- results = lapack_routine(n, n, a, n, pivots, 0)
- sign = Numeric.add.reduce(Numeric.not_equal(pivots,
- Numeric.arrayrange(1, n+1))) % 2
- return (1.-2.*sign)*Numeric.multiply.reduce(Numeric.diagonal(a))
-
-
- # Linear Least Squares
-
- def linear_least_squares(a, b, rcond=1.e-10):
- """solveLinearLeastSquares(a,b) returns x,resids,rank,s
- where x minimizes 2-norm(|b - Ax|)
- resids is the sum square residuals
- rank is the rank of A
- s is an rank of the singual values of A in desending order
-
- If b is a matrix then x is also a matrix with corresponding columns.
- If the rank of A is less than the number of columns of A or greater than
- the numer of rows, then residuals will be returned as an empty array
- otherwise resids = sum((b-dot(A,x)**2).
- Singular values less than s[0]*rcond are treated as zero.
- """
- one_eq = len(b.shape) == 1
- if one_eq:
- b = b[:, Numeric.NewAxis]
- _assertRank2(a, b)
- m = a.shape[0]
- n = a.shape[1]
- n_rhs = b.shape[1]
- ldb = max(n,m)
- if m != b.shape[0]:
- raise LinAlgError, 'Incompatible dimensions'
- t =_commonType(a, b)
- real_t = _array_type[0][_array_precision[t]]
- bstar = Numeric.zeros((ldb,n_rhs),t)
- bstar[:b.shape[0],:n_rhs] = copy.copy(b)
- a,bstar = _castCopyAndTranspose(t, a, bstar)
- lwork = 8*min(n,m) + max([2*min(m,n),max(m,n),n_rhs])
- s = Numeric.zeros((min(m,n),),real_t)
- work = Numeric.zeros((lwork,), t)
- if _array_kind[t] == 1: # Complex routines take different arguments
- lapack_routine = lapack_lite.zgelss
- rwork = Numeric.zeros((5*min(m,n)-1,), real_t)
- results = lapack_routine( m, n, n_rhs, a, m, bstar,ldb , s, rcond,
- 0,work,lwork,rwork,0 )
- else:
- lapack_routine = lapack_lite.dgelss
- results = lapack_routine( m, n, n_rhs, a, m, bstar,ldb , s, rcond,
- 0,work,lwork,0 )
- if results['info'] > 0:
- raise LinAlgError, 'SVD did not converge in Linear Least Squares'
- resids = Numeric.array([],t)
- if one_eq:
- x = copy.copy(Numeric.ravel(bstar)[:n])
- if (results['rank']==n) and (m>n):
- resids = Numeric.array([Numeric.sum((Numeric.ravel(bstar)[n:])**2)])
- else:
- x = copy.copy(Numeric.transpose(bstar)[:n,:])
- if (results['rank']==n) and (m>n):
- resids = copy.copy(Numeric.sum((Numeric.transpose(bstar)[n:,:])**2))
- return x,resids,results['rank'],copy.copy(s[:min(n,m)])
-
-
- if __name__ == '__main__':
- from Numeric import *
-
- def test(a, b):
-
- print "All numbers printed should be (almost) zero:"
-
- x = solve_linear_equations(a, b)
- check = b - matrixmultiply(a, x)
- print check
-
-
- a_inv = inverse(a)
- check = matrixmultiply(a, a_inv)-identity(a.shape[0])
- print check
-
-
- ev = eigenvalues(a)
-
- evalues, evectors = eigenvectors(a)
- check = ev-evalues
- print check
-
- evectors = transpose(evectors)
- check = matrixmultiply(a, evectors)-evectors*evalues
- print check
-
-
- u, s, vt = singular_value_decomposition(a)
- check = a - Numeric.matrixmultiply(u*s, vt)
- print check
-
-
- a_ginv = generalized_inverse(a)
- check = matrixmultiply(a, a_ginv)-identity(a.shape[0])
- print check
-
-
- det = determinant(a)
- check = det-multiply.reduce(evalues)
- print check
-
- x, residuals, rank, sv = linear_least_squares(a, b)
- check = b - matrixmultiply(a, x)
- print check
- print rank-a.shape[0]
- print sv-s
-
- a = array([[1.,2.], [3.,4.]])
- b = array([2., 1.])
- test(a, b)
-
- a = a+0j
- b = b+0j
- test(a, b)
-
-